import 'dart:async';
import 'dart:convert';
import 'dart:ui' as ui;
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_components_web/utils/utils.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import '../../model/group_dynamic_model.dart';

// 节点名称
const nodeNameImage = 'Image';
const nodeNameGroup = 'Group';
const nodeNameText = 'Text';
const nodeNameRect = 'Rect';
const nodeNameDataValue = "dataValue";
const nodeNameDeviceCode = "device_code";
const nodeNameDatetime = "datetime";
const attrNameStackedLine = "StackedLine";
const attrNameStackedBar = "StackedBar";
const attrNameStackedLineBackground = "StackedLineBackground";
const attrNameStackedBarBackground = "StackedBarBackground";
const chartBg = "assets/images/img.png";

const maxNodeNumber = 200;
// const groupId = 15;
const groupId = 73;
const token =
    'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjcmVhdGVkQXQiOjE3NTAwNTg2MzIxOTMsImlzQWRtaW4iOnRydWUsInR5cGUiOjAsImV4cGlyZSI6NzIwMCwidXNlcm5hbWUiOiIxODM2NzY5MTczMSJ9.GRxow941B6dmVkWJbwvoK6P1j1A8LrY3CDT-euCAYFo';

Future<void> main() async {
  Dio dio = Dio();
  final res = await dio.request(
    // "https://ygk.deman.cn/api/v2/device/group?id=225",
    "https://ygk.deman.cn/api/v2/device/group?id=$groupId",
    // "https://ygk.deman.cn/api/v2/device/group?id=15",
    options: Options(headers: {"authorization": "Bearer $token"}),
  );
  final j1 = res.data["data"]["content"].replaceAll('\\', '');
  final j2 = j1.replaceAll('"{"', '{"');
  final j3 = j2.replaceAll(']}"', ']}');
  print(j3);
  final model = GroupDynamicModel.fromJson(jsonDecode(j3));
  runApp(MyApp(model));
}

class MyApp extends StatelessWidget {
  final GroupDynamicModel model;

  const MyApp(this.model, {super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: HomePage(model));
  }
}

class HomePage extends StatelessWidget {
  final GroupDynamicModel model;

  const HomePage(this.model, {super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(body: CustomCanvasPaint(model));
  }
}

class CustomCanvasPaint extends StatefulWidget {
  final GroupDynamicModel model;

  const CustomCanvasPaint(this.model, {super.key});

  @override
  State<CustomCanvasPaint> createState() => _CustomCanvasPaintState();
}

class _CustomCanvasPaintState extends State<CustomCanvasPaint> {
  Single get stage => widget.model.single!;
  late WebSocketChannel channel;

  List<SingleChild> get children => widget.model.single!.children!;

  // 实时数据
  late List<Map<String, dynamic>> onlineData;
  late bool isLoading;
  late Timer nowTimer;
  late String nowTimeStr;
  final List<String> weeks = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];

  @override
  void initState() {
    nowTimeStr = "";
    // 横屏显示
    SystemChrome.setPreferredOrientations([
      DeviceOrientation.landscapeLeft,
      DeviceOrientation.landscapeRight,
    ]);
    // websocket
    channel = IOWebSocketChannel.connect(
      "wss://ygk.deman.cn:9006/ws/device/group/$groupId?token=Bearer%20$token",
      // "wss://ygk.deman.cn:9006/ws/device/group/15?token=Bearer%20$token",
    );
    onlineData = [];
    isLoading = true;
    channel.stream.listen((message) {
      onlineData.add(json.decode(message) as Map<String, dynamic>);
      // 防止点数过多
      if (onlineData.length > maxNodeNumber) {
        onlineData.removeRange(0, 50);
      }
      setState(() {});
    });
    initCanvasData().then((_) {
      isLoading = false;
      setState(() {});
    });
    nowTimer = Timer.periodic(Duration(seconds: 1), (timer) {
      final now = DateTime.now();
      final year = now.year;
      final month = getPadZero(now.month);
      final day = getPadZero(now.day);
      final hour = getPadZero(now.hour);
      final minute = getPadZero(now.minute);
      final seconds = getPadZero(now.second);
      final week = now.weekday;
      nowTimeStr = "$year-$month-$day $hour:$minute:$seconds ${weeks[week]}";
      setState(() {});
    });
    super.initState();
  }

  String getPadZero(int num) {
    return num > 10 ? "$num" : "0$num";
  }

  @override
  void dispose() {
    channel.sink.close();
    nowTimer.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return InteractiveViewer(
      minScale: 0.1,
      maxScale: 5,
      boundaryMargin: EdgeInsets.all(double.infinity),
      child:
          isLoading
              ? Center(child: CircularProgressIndicator())
              : CustomPaint(
                size: Size(double.infinity, double.infinity),
                painter: MyPainter(
                  stage: stage,
                  onlineData: onlineData,
                  nowTimeStr: nowTimeStr,
                ),
              ),
    );
  }

  // 获取历史数据
  // Future<void>

  Future<ui.Image> loadNetworkImage(String imageUrl) async {
    final Dio dio = Dio();
    try {
      final response = await dio.get<List<int>>(
        imageUrl,
        options: Options(responseType: ResponseType.bytes),
      );

      if (response.statusCode == 200 && response.data != null) {
        final Completer<ui.Image> completer = Completer();
        ui.decodeImageFromList(
          Uint8List.fromList(response.data!),
          completer.complete,
        );
        return await completer.future;
      } else {
        throw Exception('Failed to load image: ${response.statusCode}');
      }
    } catch (e) {
      throw Exception('Error loading image: $e');
    }
  }

  Future<ui.Image> loadAssetsImage(String asset) async {
    final byteData = await rootBundle.load(asset);
    final codec = await ui.instantiateImageCodec(byteData.buffer.asUint8List());
    final frame = await codec.getNextFrame();
    return frame.image;
  }

  Future<void> initCanvasData() async {
    for (final single in children) {
      for (final purpleChild in single.children!) {
        if ((purpleChild.attrs!.name! == attrNameStackedLine ||
                purpleChild.attrs!.name! == attrNameStackedBar) &&
            purpleChild.className == nodeNameGroup) {
          for (final child in purpleChild.children!) {
            if ((child.attrs!.name == attrNameStackedLineBackground ||
                    child.attrs!.name == attrNameStackedBarBackground) &&
                child.className == nodeNameImage) {
              final image = await loadAssetsImage(chartBg);
              child.image = image;
            }
          }
        } else if (purpleChild.className == nodeNameImage) {
          final image = await loadNetworkImage(purpleChild.attrs!.url!);
          purpleChild.image = image;
        }
      }
    }
  }
}

class MyPainter extends CustomPainter {
  final Single stage;
  final List<Map<String, dynamic>> onlineData;
  final String nowTimeStr;

  MyPainter({
    required this.stage,
    required this.onlineData,
    required this.nowTimeStr,
  });

  @override
  paint(Canvas canvas, Size size) {
    canvas.scale(stage.attrs!.scaleX!, stage.attrs!.scaleY!);

    canvas.drawColor(
      hexToColor(stage.attrs!.backgroundColor!),
      BlendMode.srcOver,
    );

    // 相对于Stage的X、Y方向上的偏移
    final offsetX = stage.attrs!.x! / stage.attrs!.scaleX!;
    final offsetY = stage.attrs!.y! / stage.attrs!.scaleY!;
    for (final single in stage.children!) {
      for (final purpleChild in single.children!) {
        _drawPurpleChild(purpleChild, canvas, offsetX, offsetY);
      }
    }
  }

  @override
  bool shouldRepaint(MyPainter oldDelegate) {
    return true;
  }

  _drawPurpleChild(
    PurpleChild child,
    Canvas canvas,
    double offsetX,
    double offsetY,
  ) {
    if (child.className == nodeNameImage) {
      final imgWidth = child.image!.width.toDouble();
      final imgHeight = child.image!.height.toDouble();
      canvas.drawImageRect(
        child.image!,
        Rect.fromLTWH(0, 0, imgWidth, imgHeight),
        Rect.fromLTWH(
          offsetX + child.attrs!.x!,
          offsetY + child.attrs!.y!,
          (child.attrs!.width ?? imgWidth) * (child.attrs!.scaleX ?? 1.0),
          (child.attrs!.height ?? imgHeight) * (child.attrs!.scaleY ?? 1.0),
        ),
        Paint(),
      );
    } else if (child.className == nodeNameText) {
      String preText = child.attrs!.text!;
      if (child.attrs!.name == nodeNameDatetime) {
        preText = nowTimeStr;
      }
      _drawCanvasText(
        fontSize:
            (child.attrs!.fontSize ?? 12.0).toDouble() *
            (child.attrs!.scaleY ?? 1.0),
        canvas: canvas,
        color: hexToColor(child.attrs!.fill!),
        text: preText,
        offset: Offset(offsetX + child.attrs!.x!, offsetY + child.attrs!.y!),
      );
    } else if (child.className == nodeNameGroup) {
      for (final fluffyChild in child.children!) {
        _drawFluffyChild(fluffyChild, child, canvas, offsetX, offsetY);
      }
    } else {
      print("2 - ${child.className}");
    }
  }

  void _drawFluffyChild(
    FluffyChild child,
    PurpleChild group,
    Canvas canvas,
    double offsetX,
    double offsetY,
  ) {
    final groupX = group.attrs!.x ?? 0;
    final groupY = group.attrs!.y ?? 0;
    final groupScaleX = group.attrs!.scaleX ?? 1;
    final groupScaleY = group.attrs!.scaleY ?? 1;
    if (child.className == nodeNameText) {
      // 设备参数（瞬时流量 18.53 Nm3/min）
      String preValue = child.attrs!.text!;
      if (child.attrs!.name! == nodeNameDataValue) {
        preValue = group.attrs!.text ?? "____";
        for (final data in onlineData.reversed) {
          if (data[nodeNameDeviceCode] == group.attrs!.device!.code!) {
            final code = group.attrs!.dataPoint!.code!;
            if (data.containsKey(code) && data[code] != null) {
              preValue = data[code].toString();
              break;
            }
          }
        }
      }
      _drawCanvasText(
        fontSize:
            (child.attrs!.fontSize ?? 12) *
            (child.attrs!.scaleX ?? 1) *
            groupScaleX,
        canvas: canvas,
        color: hexToColor(child.attrs!.fill!),
        text: preValue,
        offset: Offset(
          offsetX + child.attrs!.x! * groupScaleX + groupX,
          offsetY + child.attrs!.y! * groupScaleX + groupY,
        ),
      );
    } else if (child.className == nodeNameRect) {
      _drawRect(
        cornerRadius: child.attrs!.cornerRadius,
        canvas: canvas,
        color: child.attrs!.fill ?? '',
        rectLeft: groupX + offsetX,
        rectTop: groupY + offsetY,
        width: (child.attrs!.width ?? 0).toDouble() * groupScaleX.toDouble(),
        height: (child.attrs!.height ?? 0).toDouble() * groupScaleY.toDouble(),
      );
    } else if (child.className == nodeNameGroup) {
      final childGroupScaleX = child.attrs!.scaleX ?? 1;
      final childGroupScaleY = child.attrs!.scaleY ?? 1;
      final childGroupX = child.attrs!.x ?? 0;
      final childGroupY = child.attrs!.y ?? 0;
      for (final childItem in child.children!) {
        final tentacledScaleX = childItem.attrs!.scaleX ?? 1;
        final tentacledScaleY = childItem.attrs!.scaleY ?? 1;
        if (childItem.className == nodeNameRect) {
          _drawRect(
            cornerRadius: childItem.attrs!.cornerRadius,
            canvas: canvas,
            color: childItem.attrs!.fill ?? '',
            rectLeft: groupX + offsetX,
            rectTop: groupY + offsetY,
            width:
                (childItem.attrs!.width ?? 0) *
                groupScaleX *
                childGroupScaleX *
                tentacledScaleX,
            height:
                (childItem.attrs!.height ?? 0) *
                groupScaleY *
                childGroupScaleY *
                tentacledScaleY,
          );
        } else if (childItem.className == nodeNameText) {
          _drawCanvasText(
            fontSize:
                (childItem.attrs!.fontSize ?? 12.0) *
                (childItem.attrs!.scaleX ?? 1.0) *
                groupScaleX *
                childGroupScaleX,
            canvas: canvas,
            color: hexToColor(childItem.attrs!.fill!),
            text: childItem.attrs!.text ?? "",
            offset: Offset(
              offsetX +
                  childItem.attrs!.x! * groupScaleX * childGroupScaleX +
                  childGroupX * groupScaleX +
                  groupX,
              offsetY +
                  childItem.attrs!.y! * groupScaleY * childGroupScaleY +
                  childGroupY * groupScaleY +
                  groupY,
            ),
          );
        }
      }
    } else if (child.className == nodeNameImage &&
        (child.attrs!.name == attrNameStackedLineBackground ||
            child.attrs!.name == attrNameStackedBarBackground)) {
      // 图表
      double imgWidth = child.image!.width.toDouble();
      double imgHeight = child.image!.height.toDouble();
      canvas.drawImageRect(
        child.image!,
        Rect.fromLTWH(0, 0, imgWidth, imgHeight),
        Rect.fromLTWH(
          offsetX + (child.attrs!.x ?? 0) + groupX,
          offsetY + (child.attrs!.y ?? 0) + groupY,
          (child.attrs!.width ?? imgWidth) *
              (child.attrs!.scaleX ?? 1.0) *
              groupScaleX,
          (child.attrs!.height ?? imgHeight) *
              (child.attrs!.scaleY ?? 1.0) *
              groupScaleY,
        ),
        Paint(),
      );
    } else {
      print("3 - ${child.className} - ${child.attrs?.name}");
    }
  }

  // 绘制文本
  void _drawCanvasText({
    required double fontSize,
    required Canvas canvas,
    required String text,
    required Offset offset,
    required Color color,
  }) {
    const verticalTextY = 3;
    // 文本垂直方向偏移
    final textStyle = TextStyle(fontSize: fontSize, color: color);
    final textPainter = TextPainter(
      text: TextSpan(text: text, style: textStyle),
      textAlign: TextAlign.center,
      textDirection: TextDirection.ltr,
    );
    textPainter.layout();
    textPainter.paint(canvas, Offset(offset.dx, offset.dy - verticalTextY));
  }

  // 绘制矩形
  void _drawRect({
    required cornerRadius,
    required Canvas canvas,
    required String color,
    required double rectLeft,
    required double rectTop,
    required double width,
    required double height,
  }) {
    double topLeft = 0;
    double topRight = 0;
    double bottomLeft = 0;
    double bottomRight = 0;
    if (cornerRadius is int) {
      topLeft = cornerRadius.toDouble();
      topRight = topLeft;
      bottomLeft = topLeft;
      bottomRight = topLeft;
    } else if (cornerRadius is List<dynamic> && cornerRadius.isNotEmpty) {
      topLeft = (cornerRadius[0] as int).toDouble();
      topRight = (cornerRadius[1] as int).toDouble();
      bottomLeft = (cornerRadius[2] as int).toDouble();
      bottomRight = (cornerRadius[3] as int).toDouble();
    }
    canvas.drawRRect(
      RRect.fromRectAndCorners(
        Rect.fromLTWH(rectLeft, rectTop, width, height),
        topLeft: Radius.circular(topLeft / 2),
        topRight: Radius.circular(topRight / 2),
        bottomLeft: Radius.circular(bottomLeft / 2),
        bottomRight: Radius.circular(bottomRight / 2),
      ),
      Paint()..color = hexToColor(color),
    );
  }
}
